package com.ruoyi.ldzlsc.service.Impl;

import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.ldzlsc.domain.vo.*;
import com.ruoyi.ldzlsc.domain.ScMrp;
import com.ruoyi.ldzlsc.domain.ScMrpDetail;
import com.ruoyi.ldzlsc.domain.ScWorkOrder;
import com.ruoyi.ldzlsc.domain.ScProductionOrder;
import com.ruoyi.ldzlsc.domain.ScProductionOrderDetail;
import com.ruoyi.ldzlsc.mapper.ScProductionPlanMapper;
import com.ruoyi.ldzlsc.mapper.ScMrpMapper;
import com.ruoyi.ldzlsc.mapper.ScMrpDetailMapper;
import com.ruoyi.ldzlsc.mapper.ScWorkOrderMapper;
import com.ruoyi.ldzlsc.mapper.ScProductionOrderMapper;
import com.ruoyi.ldzlsc.mapper.ScProductionOrderDetailMapper;
import com.ruoyi.ldzlsc.service.IScProductionPlanService;
import com.ruoyi.system.api.BasicService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 生产计划Service业务层处理
 *
 * @author ruoyi
 * @date 2025-06-25
 */
@Service
public class ScProductionPlanServiceImpl implements IScProductionPlanService {
    @Autowired
    private ScProductionPlanMapper scProductionPlanMapper;

    @Autowired
    private ScMrpMapper scMrpMapper;

    @Autowired
    private ScMrpDetailMapper scMrpDetailMapper;

    @Autowired
    private ScWorkOrderMapper scWorkOrderMapper;

    @Autowired
    private ScProductionOrderMapper scProductionOrderMapper;

    @Autowired
    private ScProductionOrderDetailMapper scProductionOrderDetailMapper;

    @Resource
    private BasicService basicService;

    /**
     * 查询生产计划列表
     *
     * @param scProductionPlanVo 生产计划
     * @return 生产计划
     */
    @Override
    public List<ScProductionPlanVo> selectScProductionPlanList(ScProductionPlanVo scProductionPlanVo) {
        return scProductionPlanMapper.selectScProductionPlanList(scProductionPlanVo);
    }

    /**
     * 根据计划编码查询生产计划详情
     *
     * @param planCode 计划编码
     * @return 生产计划信息
     */
    @Override
    public ScProductionPlanVo selectScProductionPlanByPlanCode(String planCode) {
        return scProductionPlanMapper.selectScProductionPlanByPlanCode(planCode);
    }

    @Override
    public List<ScProductionPlanDetailVo> selectPlanDetailListByPlanCode(String planCode) {
        return scProductionPlanMapper.selectPlanDetailListByPlanCode(planCode);
    }

    @Override
    public List<ScMaterialVo> selectMaterialListByPlanCode(String planCode) {
        return scMrpDetailMapper.selectMaterialListByPlanCode(planCode);
    }

    /**
     * 新增生产计划
     *
     * @param scProductionPlanVo 生产计划信息
     * @return 结果
     */
    @Override
    @Transactional
    public int insertProductionPlan(ScProductionPlanVo scProductionPlanVo) {

        // 设置默认状态为"未分解"
        scProductionPlanVo.setStatus("0");

        // 设置创建者
        scProductionPlanVo.setCreateBy(SecurityUtils.getUsername());

        // 插入计划主表
        int rows = scProductionPlanMapper.insertScProductionPlan(scProductionPlanVo);

        // 插入计划明细
        if (rows > 0 && scProductionPlanVo.getProductId() != null) {
            scProductionPlanMapper.insertScProductionPlanDetail(
                scProductionPlanVo.getProductionPlanId(),
                scProductionPlanVo.getProductId(),
                scProductionPlanVo.getPlannedQty(),
                scProductionPlanVo.getPlanStartTime(),
                scProductionPlanVo.getPlanEndTime(),
                scProductionPlanVo.getRequiredDate()
            );
        }

        // 如果生产计划创建成功且关联了生产订单，自动更新生产订单状态
        if (rows > 0 && scProductionPlanVo.getProductionOrderId() != null) {
            updateProductionOrderStatusToPlanned(scProductionPlanVo.getProductionOrderId());
        }

        return rows;
    }

    /**
     * 分解生产计划，执行MRP运算并生成工单。
     * 整个过程被封装在事务中，确保数据一致性。
     *
     * @param planCode 生产计划编码
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void decomposePlan(String planCode) {
        // 步骤 1: 根据planCode查询生产计划详情
        ScProductionPlanVo plan = scProductionPlanMapper.selectScProductionPlanByPlanCode(planCode);
        if (plan == null) {
            throw new ServiceException("生产计划不存在: " + planCode);
        }

        // 步骤 1.1: 查询计划明细以获取准确的 productId
        List<ScProductionPlanDetailVo> planDetails = scProductionPlanMapper.selectPlanDetailListByPlanCode(planCode);
        if (planDetails == null || planDetails.isEmpty()) {
            throw new ServiceException("生产计划没有找到任何产品明细: " + planCode);
        }
        // 使用第一条明细的产品ID进行后续操作
        Long productId = planDetails.get(0).getProductId();
        if (productId == null) {
            throw new ServiceException("生产计划明细中缺少产品ID");
        }
        // 将 productId 设置回 plan 对象，
        plan.setProductId(productId);

        // 步骤 2: 根据产品ID查询其有效BOM
        ScBomVo bom = scProductionPlanMapper.selectActiveBomByProductId(plan.getProductId());
        if (bom == null || bom.getDetails() == null || bom.getDetails().isEmpty()) {
            throw new ServiceException("产品没有找到默认的、有效的BOM: " + plan.getProductId());
        }

        // 步骤 3: 计算物料毛需求
        Map<Long, BigDecimal> grossRequirements = calculateGrossRequirements(plan, bom);

        // 步骤 4: 计算物料净需求
        RequirementCalculationResult calcResult = calculateNetRequirements(grossRequirements);

        // 步骤 5: 将运算结果插入到数据库
        saveMrpResults(plan, calcResult, bom);

        // 步骤 6: 为该计划生成主工单
        createWorkOrderForPlan(plan);

        // 步骤 7: 更新生产计划状态为 "已分解"
        scProductionPlanMapper.updateScProductionPlanStatus(plan.getPlanCode(), "1");
    }


    /**
     * 步骤3: 计算毛需求。
     * 遍历BOM清单，将生产计划数量与BOM中的单位用量相乘，得到每种物料的毛需求总量。
     *
     * @param plan 生产计划
     * @param bom  物料清单
     * @return 一个包含物料ID及其对应毛需求量的Map
     */
    private Map<Long, BigDecimal> calculateGrossRequirements(ScProductionPlanVo plan, ScBomVo bom) {
        Map<Long, BigDecimal> grossRequirements = new HashMap<>();
        BigDecimal plannedQty = plan.getPlannedQty();

        for (ScBomDetailVo detail : bom.getDetails()) {
            BigDecimal materialUsage = detail.getMaterialUsage();
            if (materialUsage == null) {
                // 记录日志
                System.err.println("警告: 物料ID=" + detail.getMaterialId() + 
                    ", 名称=" + detail.getProductName() + 
                    " 的BOM用量为null，已默认设置为0");
                materialUsage = BigDecimal.ZERO;
            }
            
            BigDecimal requiredQty = plannedQty.multiply(materialUsage);
            grossRequirements.merge(detail.getMaterialId(), requiredQty, BigDecimal::add);
        }
        return grossRequirements;
    }

    /**
     * 步骤4: 计算净需求。
     * 根据毛需求清单，批量查询库存，然后计算出净需求（毛需求 - 库存）。
     *
     * @param grossRequirements 毛需求Map，包含物料ID和对应的毛需求数量
     * @return RequirementCalculationResult 计算结果，包含净需求Map和库存Map
     */
    private RequirementCalculationResult calculateNetRequirements(Map<Long, BigDecimal> grossRequirements) {
        // 提取毛需求中的物料ID列表
        List<Long> materialIds = new ArrayList<>(grossRequirements.keySet());
        // 初始化库存数量Map
        Map<Long, BigDecimal> stockQuantities = new HashMap<>();

        // 如果物料ID列表不为空，则查询库存信息
        if (!materialIds.isEmpty()) {
            // 批量查询物料库存信息
            List<ScItemStockVo> stockList = scProductionPlanMapper.selectItemStockByMaterialIds(materialIds);
            // 将库存信息转换为Map形式，键为物料ID，值为库存数量
            stockQuantities = stockList.stream()
                    .collect(Collectors.toMap(ScItemStockVo::getItemId, ScItemStockVo::getTotalQuantity, (v1, v2) -> v1));
        }

        // 初始化净需求Map
        Map<Long, BigDecimal> netRequirements = new HashMap<>();
        // 遍历毛需求，计算净需求
        for (Map.Entry<Long, BigDecimal> entry : grossRequirements.entrySet()) {
            Long materialId = entry.getKey();
            BigDecimal grossQty = entry.getValue();
            // 获取对应物料的库存数量，若无库存则默认为0
            BigDecimal stockQty = stockQuantities.getOrDefault(materialId, BigDecimal.ZERO);
            // 计算净需求，即毛需求减去库存数量
            BigDecimal netQty = grossQty.subtract(stockQty);

            // 如果净需求大于0，则加入到净需求Map中
            if (netQty.compareTo(BigDecimal.ZERO) > 0) {
                netRequirements.put(materialId, netQty);
            }
        }
        // 返回计算结果，包含净需求Map和库存Map
        return new RequirementCalculationResult(netRequirements, stockQuantities);
    }

    /**
     * 步骤5: 保存MRP运算结果。
     * 如果净需求列表不为空，则创建MRP主记录和明细记录，并存入数据库。
     *
     * @param plan              生产计划
     * @param calcResult        净需求计算结果
     * @param bom               物料清单，用于获取工序ID
     */
    private void saveMrpResults(ScProductionPlanVo plan, RequirementCalculationResult calcResult, ScBomVo bom) {
        Map<Long, BigDecimal> netRequirements = calcResult.netRequirements;
        if (netRequirements.isEmpty()) {
            return;
        }

        // 5.1 创建并插入MRP主记录
        ScMrp mrp = new ScMrp();
        mrp.setProductionPlanId(plan.getProductionPlanId());
        mrp.setMrpCode("MRP" + plan.getPlanCode());
        mrp.setRunTime(new Date());
        mrp.setCreateBy(SecurityUtils.getUsername());
        scMrpMapper.insertScMrp(mrp);

        // 5.2 准备MRP明细记录列表
        List<ScMrpDetail> mrpDetails = new ArrayList<>();
        Map<Long, BigDecimal> stockQuantities = calcResult.stockQuantities;

        //  遍历BOM中的每一个明细，而不是遍历聚合后的净需求
        for (ScBomDetailVo bomDetail : bom.getDetails()) {
            Long materialId = bomDetail.getMaterialId();
            
            // 检查这个物料是否存在净需求（即是否短缺）
            if (netRequirements.containsKey(materialId)) {
                // 这个物料在BOM中的单位用量
                BigDecimal usagePerProduct = bomDetail.getMaterialUsage();
                if (usagePerProduct == null) continue; // 如果BOM用量为空，则跳过

                // 根据BOM用量计算此工序的毛需求
                BigDecimal grossQtyForProcess = plan.getPlannedQty().multiply(usagePerProduct);


                BigDecimal stockQty = stockQuantities.getOrDefault(materialId, BigDecimal.ZERO);
                BigDecimal shortQty = netRequirements.get(materialId);


                ScMrpDetail detail = new ScMrpDetail();
                detail.setMrpId(mrp.getMrpId());
                detail.setMaterialId(materialId);
                // 记录此工序的毛需求量
                detail.setRequiredQty(grossQtyForProcess);
                detail.setAvailableQty(stockQty);
                detail.setShortageQty(shortQty);
                detail.setSuggestAction("生产/采购");
                detail.setCreateBy(SecurityUtils.getUsername());
                // 设置当前BOM明细行对应的工序ID
                detail.setProcessId(bomDetail.getOperationalId());
                
                mrpDetails.add(detail);
            }
        }

        // 5.3 批量插入MRP明细
        if (!mrpDetails.isEmpty()) {
            scMrpDetailMapper.batchInsertScMrpDetail(mrpDetails);
        }
    }

    /**
     * 步骤6: 为生产计划创建主工单。
     *
     * @param plan 生产计划
     */
    private void createWorkOrderForPlan(ScProductionPlanVo plan) {
        ScWorkOrder workOrder = new ScWorkOrder();
        workOrder.setWorkOrderCode(generateCode(4L));
        workOrder.setWorkOrderName(plan.getPlanName()); // 设置工单名称
        workOrder.setProductionPlanId(plan.getProductionPlanId());
        workOrder.setProductId(plan.getProductId());
        workOrder.setQtyNum(plan.getPlannedQty());
        workOrder.setStatus("0"); // 0-未开始
        workOrder.setWorkshopId(plan.getWorkshopId()); // 设置车间ID
        workOrder.setProductionLineId(plan.getProductionLineId()); // 设置生产线ID
        workOrder.setTeamId(null); // 设置班组ID
        workOrder.setPlanStartTime(plan.getPlanStartTime()); // 设置计划开始时间
        workOrder.setPlanEndTime(plan.getPlanEndTime()); // 设置计划结束时间
        workOrder.setSourceDocNo(plan.getOrderCode()); // 设置来源单号
        workOrder.setRemark(plan.getRemark()); // 设置备注
        workOrder.setCreateBy(SecurityUtils.getUsername());
        scWorkOrderMapper.insertScWorkOrder(workOrder);
    }

    /**
     * 远程调用基础服务生成编码，并健壮地处理返回结果。
     * @param type 编码类型
     * @return 生成的编码字符串
     */
    private String generateCode(Long type) {
        // 调用远程服务（消费模式，确保每次调用都会递增流水号）
        AjaxResult codeResult = basicService.automaticallyNumbersConsume(type);

        // 确保远程调用返回了结果对象
        if (codeResult == null) {
            throw new ServiceException(String.format("远程获取编码调用失败，返回结果为空，类型: %d", type));
        }

        // 根据该接口的特殊设计，编码直接存放在msg字段
        Object msg = codeResult.get(AjaxResult.MSG_TAG);
        Object code=codeResult.get(AjaxResult.CODE_TAG);
        System.out.println("编码是："+code);
        // 确保编码内容不为空
        if (msg == null || msg.toString().isEmpty()) {
            throw new ServiceException(String.format("远程获取编码成功，但返回的编码为空，类型: %d, 返回信息: %s", type, msg));
        }

        // 从AjaxResult中正确取出msg部分作为编码
        return msg.toString();
    }

    /**
     * 用于封装第4步计算结果的内部类, 避免在方法间传递过多参数。
     */
    private static class RequirementCalculationResult {
        final Map<Long, BigDecimal> netRequirements;
        final Map<Long, BigDecimal> stockQuantities;

        RequirementCalculationResult(Map<Long, BigDecimal> netRequirements, Map<Long, BigDecimal> stockQuantities) {
            this.netRequirements = netRequirements;
            this.stockQuantities = stockQuantities;
        }
    }

    @Override
    @Transactional
    public int updateProductionPlan(ScProductionPlanVo scProductionPlanVo) {
        scProductionPlanVo.setUpdateBy(SecurityUtils.getUsername());
        // 更新计划主表
        int rows = scProductionPlanMapper.updateProductionPlan(scProductionPlanVo);
        if (rows > 0) {
            // 更新计划明细表
            scProductionPlanMapper.updateProductionPlanDetail(scProductionPlanVo);
        }
        return rows;
    }

    @Override
    @Transactional
    public int deleteProductionPlanByIds(Long[] productionPlanIds) {
        // 先删除明细
        scProductionPlanMapper.deleteProductionPlanDetailByIds(productionPlanIds);
        // 再删除主表
        return scProductionPlanMapper.deleteProductionPlanByIds(productionPlanIds);
    }

    /**
     * 根据生产订单ID创建生产计划
     *
     * @param productionOrderId 生产订单ID
     * @return 生产计划信息
     */
    @Override
    public ScProductionPlanVo createPlanFromProductionOrder(Long productionOrderId) {
        // 查询生产订单信息
        ScProductionOrder order = scProductionOrderMapper.selectScProductionOrderById(productionOrderId);
        if (order == null) {
            throw new ServiceException("生产订单不存在: " + productionOrderId);
        }

        // 查询订单明细
        List<ScProductionOrderDetail> orderDetails = scProductionOrderDetailMapper.selectScProductionOrderDetailByOrderId(productionOrderId);
        if (orderDetails == null || orderDetails.isEmpty()) {
            throw new ServiceException("生产订单没有产品明细: " + productionOrderId);
        }

        // 创建生产计划VO对象
        ScProductionPlanVo planVo = new ScProductionPlanVo();

        // 设置基本信息
        planVo.setPlanName("关联订单[" + order.getProdOrderCode() + "]的生产计划");
        planVo.setProductionOrderId(productionOrderId);
        planVo.setSourceType("生产订单");
        planVo.setRemark("关联生产订单[" + order.getProdOrderCode() + "]创建");

        // 如果只有一个产品，直接设置产品信息
        if (orderDetails.size() == 1) {
            ScProductionOrderDetail detail = orderDetails.get(0);
            planVo.setProductId(detail.getProductId());
            planVo.setPlannedQty(detail.getQtyNum());

            // 设置时间信息
            if (detail.getDeliveryDate() != null) {
                planVo.setRequiredDate(detail.getDeliveryDate());
                planVo.setPlanEndTime(detail.getDeliveryDate());
            }
        }
        return planVo;
    }

    /**
     * 更新生产订单状态为"已计划"
     *
     * @param productionOrderId 生产订单ID
     */
    private void updateProductionOrderStatusToPlanned(Long productionOrderId) {
        try {
            // 查询当前生产订单状态
            String currentStatus = scProductionOrderMapper.selectProductionOrderStatusById(productionOrderId);

            if (currentStatus == null) {
                System.out.println("未找到生产订单，ID: " + productionOrderId);
                return;
            }

            System.out.println("当前状态是："+currentStatus);

            // 只有状态为"1-待计划"的生产订单才更新为"2-已计划"
            if ("1".equals(currentStatus)) {
                int rows = scProductionOrderMapper.updateProductionOrderStatus(productionOrderId, "2");
                if (rows > 0) {
                    System.out.println("生产订单状态更新成功，ID: {}, 状态: 1-待计划 → 2-已计划"+productionOrderId);
                } else {
                    System.out.println("生产订单状态更新失败，ID: {}, 状态: 1-待计划 → 2-已计划"+productionOrderId);
                }
            } else {
                System.out.println("生产订单状态不是待计划，不能更新，ID:"+productionOrderId+" 状态: "+ currentStatus);
            }
        } catch (Exception e) {
            System.out.println("更新生产订单状态异常，ID: " + productionOrderId + " 错误信息: " + e.getMessage());
        }
    }
}